import datetime
import logging

from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response

from . import edu_admin
from .models import StudentInfo, CourseItem
from .serializers import StudentInfoSerializer, CourseItemSerializer
from .tasks import get_course_item
from django.contrib.auth import get_user_model


class StudentInfoViewSet(viewsets.ModelViewSet):
    serializer_class = StudentInfoSerializer
    permission_classes = [IsAuthenticated]
    filterset_fields = ["qq", "vx", "dify_user_id", "student_id"]

    identifier_fields = ("dify_user_id", "qq", "vx", "student_id")

    def get_queryset(self):
        # 只允许当前登录用户访问自己的绑定记录
        queryset = StudentInfo.objects.filter(user=self.request.user)
        filters = self._extract_identifier_filters(self.request.query_params)
        if not filters and self.action != "create":
            return queryset.none()
        return queryset.filter(**filters) if filters else queryset

    @classmethod
    def _extract_identifier_filters(cls, data):
        filters = {}
        for field in cls.identifier_fields:
            value = data.get(field)
            if value:
                filters[field] = value
        return filters

    def create(self, request, *args, **kwargs):
        student_id = request.data.get("student_id")
        if not student_id:
            return Response({"message": "学号缺失"}, status=status.HTTP_400_BAD_REQUEST)
        if not request.data.get("student_password"):
            return Response({"message": "密码缺失"}, status=status.HTTP_400_BAD_REQUEST)

        # 校验学号唯一性：若其他用户已绑定相同学号，提示冲突
        User = get_user_model()
        existed_user = User.objects.filter(student_id=student_id).exclude(id=request.user.id).first()
        if existed_user:
            return Response({"message": "该学号已绑定其他账号，请更换或联系管理员"}, status=status.HTTP_400_BAD_REQUEST)

        # 组织请求体信息
        request_data = {key: val for key, val in request.data.items() if val}
        request_data["user"] = request.user.id

        # 通过 dify/qq/vx/student_id 查找存量记录
        existing_student_info = StudentInfo.objects.filter(user=request.user).first()

        if existing_student_info:
            # 删除绑定的所有 CourseItem
            CourseItem.objects.filter(student=existing_student_info).delete()
            # 更新 StudentInfo 信息，并将 message 字段设置为空
            request_data["message"] = ""
            # 如果存在，则更新
            serializer = self.get_serializer(existing_student_info, data=request_data, partial=True)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)
        else:
            # 如果不存在，则创建
            serializer = self.get_serializer(data=request_data)
            serializer.is_valid(raise_exception=True)
            self.perform_create(serializer)

        # 将学号写入用户信息（覆盖空值）
        if not request.user.student_id:
            request.user.student_id = student_id
            request.user.save(update_fields=["student_id", "updated_at"])

        # 创建处理故事生成的异步任务
        task_mode = "async"
        try:
            get_course_item.delay(serializer.instance.id)
        except Exception:
            # Celery 未启动时同步执行，避免无响应
            task_mode = "sync"
            get_course_item.apply(args=(serializer.instance.id,))

        return Response(
            {
                "message": "课表抓取已开始，请稍后查询课程列表",
                "student": serializer.data,
                "task_mode": task_mode,
            },
            status=status.HTTP_201_CREATED,
        )


class CourseItemViewSet(viewsets.ModelViewSet):
    serializer_class = CourseItemSerializer
    permission_classes = [AllowAny]
    filterset_fields = ["student__qq", "student__vx", "student__dify_user_id", "student__student_id"]

    identifier_fields = {
        "qq": "student__qq",
        "vx": "student__vx",
        "dify_user_id": "student__dify_user_id",
        "student_id": "student__student_id",
    }

    def get_queryset(self):
        queryset = CourseItem.objects.select_related("student")
        filters = self._extract_identifier_filters(self.request.query_params)
        if filters:
            return queryset.filter(**filters)
        if self.kwargs.get(self.lookup_field):
            # 允许通过主键访问单条记录
            return queryset
        return queryset.none()

    def _extract_identifier_filters(self, data):
        filters = {}
        for param, lookup in self.identifier_fields.items():
            value = data.get(param)
            if value:
                filters[lookup] = value
        return filters

    def get_flag_day_course_msg(self, flag, target_weekday=1) -> str:
        """
        获取日课表信息
        Args:
            flag: 1-获取今天课表，2-获取明天课表，3-获取后天课表

        Returns: 课表信息字符串
        """

        def get_day_course_msg(day: str, w: int, d: int):
            if w > 20:
                return f"现在是第 {w} 周，还没放假？还看课表？还想上课？"
            logging.info(f"获取第 {w} 周星期 {d} 课表信息")
            # 获取该用户的所有课表信息
            user_course_item_queryset = self.filter_queryset(self.get_queryset())

            if len(user_course_item_queryset) != 0:
                # 获取用户课表中对应周数和星期数课程列表
                course_list = self.get_course_list(list(user_course_item_queryset.values()), w, d)
                if len(course_list) > 0:
                    course_list.sort(key=lambda x: x["时间"])
                    msg = f"{day} 日是第 {w} 教学周，星期 {d}。\n一共有 {len(course_list)} 节课哦~"
                    for idx, course in enumerate(course_list):
                        msg += f"\n\n第 {idx + 1} 节：{course['课程名']}"
                        msg += f"\n老师：{course['教师'].replace('*', '')}"
                        start_time = edu_admin.course_start_time_dict[course["时间"][0]]
                        end_time = edu_admin.course_start_time_dict[course["时间"][-1]]
                        end_time = datetime.datetime.strptime(end_time, "%H:%M")
                        end_time = datetime.datetime.combine(datetime.date.today(), end_time.time())
                        end_time = end_time + datetime.timedelta(minutes=45)
                        end_time = end_time.strftime("%H:%M")
                        msg += f"\n时间：{start_time}-{end_time}"
                        # 泰达>>9 为中院九号楼，西院的同学很多都不知道，因此如果出现的话单独加个备注
                        if "泰达>>9" in course["地点"]:
                            msg += f"\n地点：{course['地点']}\n（泰达>>9 为中院九号楼，即中院图书馆！）"
                        else:
                            msg += f"\n地点：{course['地点']}"
                    return msg
                return "太棒了，没有课，睡大觉~"
            else:
                return "你还没有开启课程表功能哦，发送：/课表帮助，查看使用方法"

        def get_date_by_weekday(target_weekday):
            """
            根据指定的星期数获取当前周内该天的日期。

            参数:
            target_weekday (int): 目标星期几（1表示星期一，7表示星期日）。

            返回:
            datetime.date: 当前周内指定星期几的日期。
            """
            # 获取今天的日期
            today = datetime.date.today()
            # 获取今天是星期几 (1=星期一, ..., 7=星期日)
            current_weekday = today.isoweekday()
            # 计算日期差值
            delta_days = target_weekday - current_weekday
            # 计算目标日期
            target_date = today + datetime.timedelta(days=delta_days)
            return delta_days, target_date  # 返回 日期差 和 日期

        today = datetime.date.today()
        # 获取当前周数和星期
        msg = None
        teaching_week, teaching_week_day = edu_admin.get_today_week_day()
        if target_weekday == 1:
            if flag == 1 :
                msg = get_day_course_msg(str(today), int(teaching_week), int(teaching_week_day))
            elif flag == 2:
                tomorrow = today + datetime.timedelta(days=1)
                if int(teaching_week_day) == 7:  # 如果今天是周末，则明天为下一个教学周的周一
                    teaching_week = int(teaching_week) + 1
                    teaching_week_day = 0
                msg = get_day_course_msg(str(tomorrow), int(teaching_week), int(teaching_week_day) + 1)
            elif flag == 3:
                day_after_tomorrow = today + datetime.timedelta(days=2)
                if int(teaching_week_day) == 6:  # 如果今天是周六，则后天为下一个教学周的周一
                    teaching_week = int(teaching_week) + 1
                    teaching_week_day = -1
                elif int(teaching_week_day) == 7:  # 如果今天是周末，则后天为下一个教学周的周二
                    teaching_week = int(teaching_week) + 1
                    teaching_week_day = 0
                msg = get_day_course_msg(str(day_after_tomorrow), int(teaching_week), int(teaching_week_day) + 2)
        else:
            _, target_date = get_date_by_weekday(target_weekday)
            msg = get_day_course_msg(str(target_date), int(teaching_week), int(target_weekday))
        return msg
    
    @action(detail=False, methods=["get"])
    def get_flag_day_course(self, request, *args, **kwargs):
        if not self._extract_identifier_filters(request.query_params):
            return Response(
                {"message": "请提供 qq、vx、dify_user_id 或 student_id 以查询课表"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        flag = int(request.query_params.get("flag", 1))
        weekday = int(request.query_params.get("weekday", request.query_params.get("week", 1)))
        course_msg = self.get_flag_day_course_msg(flag=flag, target_weekday=weekday)
        return Response({"message": course_msg}, status=status.HTTP_200_OK)

    def get_flag_week_course_msg(self, target_weekday=0) -> str:
        """
        获取周课表信息
        Args:
            flag: 1-获取本周课表，2-获取下周课表

        Returns: 课表信息字符串
        """
        def get_week_course_msg(day: str, w: int):
            if w > 20:
                return f"现在是第 {w} 周，还没放假？还看课表？还想上课？"
            logging.info(f"获取第 {w} 周课表信息")
            # 获取该用户的所有课表信息
            user_course_item_queryset = self.filter_queryset(self.get_queryset())

            if len(user_course_item_queryset) != 0:
                # 获取用户课表中对应周数和星期数课程列表
                course_list = self.get_course_list(list(user_course_item_queryset.values()), w)
                if len(course_list) > 0:
                    msg = f"第 {w} 教学周。\n一共有 {len(course_list)} 节课哦~"
                    for i in range(1, 8):
                        msg += f"\n\n星期{i}"
                        if any([item["weekday"] == i for item in course_list]):
                            filter_course_list = [item for item in course_list if item["weekday"] == i]
                            filter_course_list.sort(key=lambda x: x["时间"])
                            for idx, course in enumerate(filter_course_list):
                                msg += f"\n\n第 {idx + 1} 节：{course['课程名']}"
                                msg += f"\n老师：{course['教师'].replace('*', '')}"
                                start_time = edu_admin.course_start_time_dict[course["时间"][0]]
                                end_time = edu_admin.course_start_time_dict[course["时间"][-1]]
                                end_time = datetime.datetime.strptime(end_time, "%H:%M")
                                end_time = datetime.datetime.combine(datetime.date.today(), end_time.time())
                                end_time = end_time + datetime.timedelta(minutes=45)
                                end_time = end_time.strftime("%H:%M")
                                msg += f"\n时间：{start_time}-{end_time}"
                                # 泰达>>9 为中院九号楼，西院的同学很多都不知道，因此如果出现的话单独加个备注
                                if "泰达>>9" in course["地点"]:
                                    msg += f"\n地点：{course['地点']}\n（泰达>>9 为中院九号楼，即中院图书馆！）"
                                else:
                                    msg += f"\n地点：{course['地点']}"
                        else:
                            msg += "\n没有课，睡大觉~"
                    return msg
                return "震惊！一周都没有课，岂不爽歪歪？"
            else:
                return "你还没有开启课程表功能哦，发送：/课表帮助，查看使用方法"

        today = datetime.date.today()
        # 获取当前周数和星期
        msg = None
        teaching_week, _ = edu_admin.get_today_week_day()
        if target_weekday == 1:
            msg = get_week_course_msg(str(today), int(teaching_week))
        else:
            msg = get_week_course_msg(str(today), int(teaching_week) + 1)
        return msg

    @action(detail=False, methods=["get"])
    def get_flag_week_course(self, request, *args, **kwargs):
        if not self._extract_identifier_filters(request.query_params):
            return Response(
                {"message": "请提供 qq、vx、dify_user_id 或 student_id 以查询课表"},
                status=status.HTTP_400_BAD_REQUEST,
            )

        week = int(request.query_params.get("week", 1))
        course_msg = self.get_flag_week_course_msg(target_weekday=week)
        return Response({"message": course_msg}, status=status.HTTP_200_OK)

    @staticmethod
    def get_course_list(course_item_list, w: int = None, d: int = None):
        course_list = []
        for course_item in course_item_list:
            # 提取课程时间信息
            course_time = course_item["course_time"]
            course_time_week_list = edu_admin.extract_weeks(course_time)
            course_time_weekday = edu_admin.extract_weekday(course_time)
            course_time_periods = edu_admin.extract_periods(course_time)
            # 判断是否为本周课程
            if w and (not course_time_week_list or w not in course_time_week_list):
                continue
            # 判断是否为本星期课程
            if d and (not course_time_weekday or d != course_time_weekday):
                continue
            # 整理课程信息
            course_list.append({
                "课程名": course_item["name"],
                "教师": course_item["teacher"],
                "时间": course_time_periods,
                "地点": course_item["location"],
                "weekday": course_time_weekday
            })
        return course_list
